package nl.unimaas.games.tron.player;

import java.awt.Color;
import java.io.File;
import java.lang.reflect.Field;

import org.w3c.dom.NamedNodeMap;

import nl.unimaas.games.tron.engine.Board;
import nl.unimaas.games.tron.experiment.GameAnalyzer;
import nl.unimaas.games.tron.experiment.GameAnalyzer.MoveStatistics;
import nl.unimaas.games.tron.player.mcts.CutOffStrategy;
import nl.unimaas.games.tron.player.mcts.FinalNodeSelectionStrategy;
import nl.unimaas.games.tron.player.mcts.PlayoutStrategy;
import nl.unimaas.games.tron.player.mcts.SolverNode;
import nl.unimaas.games.tron.player.mcts.SolverSelectionStrategy;

@SuppressWarnings("serial")
public class BiasedSolverPlayer extends MCTSSolverPlayer {
	private final static String DEFAULT_FILE = "data" + File.separator + "global.dat";
	public final static double DEFAULT_W = 10, DEFAULT_C = UCTPlayer.DEFAULT_C;
	public final static int DEFAULT_T = UCTPlayer.DEFAULT_T;
	private MoveStatistics stats;
	
	public BiasedSolverPlayer(String name, int num, Color c) {
		super(name, num, c);
		select = new SolverSelectionStrategy.BiasedUCT(DEFAULT_C, DEFAULT_T, DEFAULT_W);
	}
	
	public void setMoveStatistics(MoveStatistics stats) {
		this.stats = stats;
	}
	
	@Override
	protected SolverNode createNode(int player, SolverNode parent, int move, Board b) {
		SolverNode n = super.createNode(player, parent, move, b);
		if (move != Board.MOVE_NONE) {
			int category = GameAnalyzer.getCategory(b, player, move);
			n.tag = stats.getTransitionalProbability(category);
		}
		return n;
	}

	@Override
	public void reset() {
		super.reset();
		if (stats == null) {
			System.err.println("BiasedMCTSSolverPlayer doesn't have stats assigned, defaulting to '" + DEFAULT_FILE + "'..");
			try {
				stats = MoveStatistics.fromFile(DEFAULT_FILE);
			}
			catch (Exception e) {
				System.err.println("Unable to load the stats file");
				e.printStackTrace();
			}
		}
	}
	
	@Override
	protected void onComputeFirstMove(SolverNode theRoot) {
		if (stats == null)
			throw new IllegalStateException("I need stats!");
	}
	
	
	public static Player fromXml(org.w3c.dom.Node playerRoot, int num, Color color) {
		return fromXml(playerRoot, new BiasedSolverPlayer(BiasedSolverPlayer.class.getSimpleName(), num, color));
	}
	public static Player fromXml(org.w3c.dom.Node playerRoot, BiasedSolverPlayer player) {
		NamedNodeMap attrs = playerRoot.getAttributes();
		player.description = "";
		if (attrs.getNamedItem("finalMove") != null) {
			try {
				Field field = FinalNodeSelectionStrategy.class.getField(attrs.getNamedItem("finalMove").getNodeValue());
				player.finalMove = (FinalNodeSelectionStrategy) field.get(null);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		if (attrs.getNamedItem("selection") != null) {
			try {
				Field field = SolverSelectionStrategy.class.getField(attrs.getNamedItem("selection").getNodeValue());
				player.select = (SolverSelectionStrategy) field.get(null);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		if (attrs.getNamedItem("playout") != null) {
			try {
				Field field = PlayoutStrategy.class.getField(attrs.getNamedItem("playout").getNodeValue());
				player.playout = (PlayoutStrategy) field.get(null);
				player.description += String.format("playout=%s", field.getName());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		if (attrs.getNamedItem("cut") != null) {
			try {
				String cutType = attrs.getNamedItem("cut").getNodeValue();
				if (cutType.equals("SPACE"))
					player.cutOff = CutOffStrategy.SPACE;
				else
					player.cutOff = null;
			} catch (Exception e) {
				player.cutOff = null;
				e.printStackTrace();
			}
		}
		else
			player.cutOff = null;
		
		if (attrs.getNamedItem("expCut") != null) {
			try {
				player.applyExpansionCutOff = Boolean.parseBoolean(attrs.getNamedItem("expCut").getNodeValue());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		else
			player.applyExpansionCutOff = false;
		
		if (attrs.getNamedItem("simCut") != null) {
			try {
				player.applySimulationCutOff = Boolean.parseBoolean(attrs.getNamedItem("simCut").getNodeValue());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		else
			player.applySimulationCutOff = false;

		if (attrs.getNamedItem("maxTurnTime") != null) {
			try {
				player.MAX_TURN_TIME = Long.parseLong(attrs.getNamedItem("maxTurnTime").getNodeValue());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		double C = DEFAULT_C;
		int T = DEFAULT_T;
		double W = DEFAULT_W;
		if (attrs.getNamedItem("C") != null) {
			try {
				C = Double.parseDouble(attrs.getNamedItem("C").getNodeValue());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		if (attrs.getNamedItem("T") != null) {
			try {
				T = Integer.parseInt(attrs.getNamedItem("T").getNodeValue());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		if (attrs.getNamedItem("W") != null) {
			try {
				W = Double.parseDouble(attrs.getNamedItem("W").getNodeValue());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		String statsFile = DEFAULT_FILE;
		if (attrs.getNamedItem("stats") != null) {
			try {
				statsFile = attrs.getNamedItem("stats").getNodeValue();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		try {
			player.stats = MoveStatistics.fromFile(statsFile);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		player.select = new SolverSelectionStrategy.BiasedUCT(C, T, W);
		player.description = String.format("select=%s playout=%s cut=%s final=%s expCut=" + player.applyExpansionCutOff + " simCut=" + player.applySimulationCutOff + "stats=%s", player.select, player.playout, player.cutOff, player.finalMove, statsFile);
		return player;
	}

	@Override
	public BiasedSolverPlayer clone() {
		BiasedSolverPlayer player = new BiasedSolverPlayer(getName(), getNumber(), getColor());
		player.select = select;
		player.finalMove = finalMove;
		player.cutOff = cutOff;
		player.playout = playout;
		player.description = description;
		player.spaceEval = spaceEval;
		player.applyExpansionCutOff = applyExpansionCutOff;
		player.applySimulationCutOff = applySimulationCutOff;
		player.applySpaceEstimation = applySpaceEstimation;
		player.stats = stats;
		return player;
	}
}
